package cs423.loadBalancer.core;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.widget.Toast;

public class TransferManager implements Runnable{

	//transfer amount per time, this amount is dynamic
	static int transferAmount=25;
	
	public TransferManager(){
	}
	
	//required implementation of this function
	@Override
	public void run() {
		Thread wd = new Thread(new waitForData());
		Thread ca = new Thread(new coor_adaptor());
		wd.setDaemon(true);
		ca.setDaemon(true);

		System.out.println("THREADS INSIDE TRANSFER MANAGER CREATED SUCCESFULLY");

		wd.start();
		ca.start();
		
		try {
			//Main.tm.lock();
			Main.tm_cv.acquire();
		} catch (InterruptedException e) {
			System.out.println( "Main.tm_cv.await(); " + e.toString());
			e.printStackTrace();
		}	finally{
			//Main.tm.unlock();
			System.out.println("WILL FORCE CLOSE THREADS IN TRANSFER");
			wd.stop();
			ca.stop();
		}
		
	}

	//This thread is always open to wait for notifications from the adaptor thread
	class coor_adaptor implements Runnable{

		@Override
		public void run() {
			while(true){
			
			try {
		//		Main.adaptor_trans_lock.lock();
				Main.trans_w_adaptor.acquire();
			} catch (InterruptedException e) {
				System.out.println( "Main.trans_w_adaptor.await() " + e.toString());
				e.printStackTrace();
			}//	finally{
				//Main.adaptor_trans_lock.unlock();
			//}
			
			//ASSUME NOT EMPTY NOW, BETTER ADD CHECK FEATURE LATER
			List<Integer> pass = new ArrayList<Integer>();
			for(int i=0; i<TransferManager.transferAmount;i++)
			{
				if(!Main.cut_pos.isEmpty())
					pass.add(Main.cut_pos.remove(0));
				else 
					break;
			}
			//pass it to the other side
			Socket new_sock;
			
			boolean repeat =  true;
			while(repeat){
			try {
				new_sock = new Socket(Main.ipAddress, Main.TRANSFERPORT);
				OutputStream out = new_sock.getOutputStream();
				DataOutputStream dos = new DataOutputStream(out);

				dos.writeInt(pass.size());

				for(int i=0; i<pass.size();i++)
					dos.writeInt(pass.get(i).intValue());
				
				System.out.println("AFTER WRITING INTEGER");
				
				repeat = false;
				
				new_sock.close();
			} catch (UnknownHostException e) {
				System.out.println( "coor_adaptor unknow host " + e.toString());
				e.printStackTrace();
			} catch (IOException e) {
				System.out.println( "coor_adaptor IO " + e.toString()+"  WILL TRY AGAIN");
				e.printStackTrace();
			}
			
			}//repeat
			
		}//while
			
		}//run
		
	}//class
	
	
	//this function waits for notifications from the Socket
	class waitForData implements Runnable{
		@Override
		public void run() {
			/**
			 * NEED TO PAY ATTENTION TO WHEN TO CLOSE THE SOCKET
			 * WHEN TO DETECT THE FINISH OF THE TRANSMISSION
			 */
			
			ServerSocket ss = null;
			
			try {
				 ss = new ServerSocket(Main.TRANSFERPORT);
			} catch (IOException e) {
				System.out.println( " ss = new ServerSocket(Main.TRANSFERPORT); " + e.toString());
				e.printStackTrace();
			}
			
			Socket OtherSide=null;
			int [] passIn = null;
			
			try {
				while((OtherSide=ss.accept())!=null)
				{
					System.out.println("ACCEPTED A NEW CONNECTION");
					//get the data
					InputStream in=null;
					try {
						in = OtherSide.getInputStream();
					} catch (IOException e) {
						e.printStackTrace();
					}
					DataInputStream dis = new DataInputStream(in);
					int size=-1;
					try {
						size = dis.readInt();
						passIn = new int[size];
						for(int i=0;i<size;i++)
							passIn[i]=dis.readInt();
						OtherSide.close();
					//Add pos(Integer) to the global Job QUEUE
					} catch (IOException e) {
						System.out.println( "size = dis.readInt() OtherSide.close();" + e.toString());
						e.printStackTrace();
					}
					
					for(int i=0; i<passIn.length;i++)
						Main.cut_pos.add(new Integer(passIn[i]));
					
				}

			} catch (IOException e) {
				System.out.println( "while((OtherSide=ss.accept())!=null) " + e.toString());
				e.printStackTrace();
			}
			
		}
		
	}
	
}
